package cn.oversky.eom.utils;

import cn.oversky.eom.IExcelObjInspector;
import cn.oversky.eom.IExcelRowConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * @email CorvusY@foxmail.com
 * @date 2017/5/31 11:41.
 * Now is history !
 */
public class ExcelBeansUtil {

    private static Logger logger = LoggerFactory.getLogger( ExcelBeansUtil.class );

    private static String SEP = ".";

    private static String SEP_SPLIT = "\\.";

    public static LeafValueSetter fitOut(Object ins, String fieldExp, IExcelRowConfig thConvert ) throws Exception {
        // 如果没有表头转换器，默认当前 属性名表达式即是属性名。
        if( thConvert == null ) {
            Field field = ReflectionUtils.getDeclaredField( ins, fieldExp );
            Type genericType = field.getGenericType();
            return new LeafValueSetter( genericType, ins, field);
        }
        // 处理表达式
        List<String> list = Arrays.asList( fieldExp.split( SEP_SPLIT ) );
        Queue<String> fieldQueue = new ConcurrentLinkedQueue<String>( list );
        return fitOut( ins, fieldQueue, thConvert );
    }

    public static LeafValueSetter fitOut(Object ins, Queue<String> fieldQueue, IExcelRowConfig thConvert )  throws  Exception {
        Class<?> type = ins.getClass();
        String fieldNameExp = fieldQueue.poll();

        // 从属性表达式中截取属性名，如果表达式所指的属性是数组，则含方括号，舍弃方括号
        String fieldName;
        int squareIdx = fieldNameExp.indexOf( "[" );
        if( squareIdx == -1 ) {
            fieldName = fieldNameExp;
        } else {
            fieldName = fieldNameExp.substring( 0, squareIdx );
        }

        Field field = type.getDeclaredField(fieldName);

        // 当认定执行到表达式最末端，则直接将现值赋值给当前对象。
        // 直接对象的赋值
        if( fieldQueue.size() == 0 ) {
            return new LeafValueSetter( field.getGenericType(), ins, field );
        }

        // 取出表达式的第一层值，如果第二次还有值，则递归第二层的赋值。
        field.setAccessible( true );
        Object fieldValue = field.get( ins );
        field.setAccessible( false );

        // 对于集合进行处理
        // 对象集合的赋值
        if( fieldValue instanceof List ) {
            return fitOutList( fieldQueue, fieldValue, fieldNameExp, fieldName, thConvert );
        } else {
            // 对子对象进行处理
            // 对象层叠的赋值
            return fitOut( fieldValue, fieldQueue, thConvert );
        }
    }

    /**
     *
     * @param fieldQueue 属性表达式所解析的属性队列
     * @param fieldValue  当前层级所获取的属性值
     * @param fieldNameExp 当前层级的属性表达式
     * @param fieldName 当前层级的属性名
     * @param thConvert 全局表头转换器
     * @throws Exception
     */
    private static LeafValueSetter fitOutList(
            Queue<String> fieldQueue,
            Object fieldValue,
            String fieldNameExp,
            String fieldName,
            IExcelRowConfig thConvert ) throws  Exception {

        // 截取表达式所指下标
        Integer idx = Integer.parseInt( fieldNameExp.replaceAll("^.*\\[", "").replaceAll("].*", "") );

        // 获取下标值的对象
        Object listEle;
        int size = ((List) fieldValue).size();
        if( size <= idx ) {
            // 获取下标所指的类型
            Class genericType = thConvert.getCollectionGenericType(fieldName);
            listEle = genericType.newInstance();

            // 获取值设置器
            LeafValueSetter leafValueSetter = fitOut(listEle, fieldQueue, thConvert);

            // 往回调中设置当前当前对象所处列表
            leafValueSetter.setCollection( (List) fieldValue );
            // 设置当前对象查看
            leafValueSetter.setInspector( thConvert.getCollectionObjInspector( fieldName ) );
            return leafValueSetter;
        } else {
            listEle = ((List) fieldValue).get(idx);
            return fitOut( listEle, fieldQueue, thConvert );
        }
    }

    public static class LeafValueSetter {

        private Type fieldType;
        private Object ins;
        private Field field;

        private boolean addFlag = false;

        private Collection collection;

        private IExcelObjInspector inspector;

        public LeafValueSetter() {
        }

        public LeafValueSetter(Type fieldType, Object ins, Field field) {
            this.fieldType = fieldType;
            this.ins = ins;
            this.field = field;
        }

        public void set(Object value ) throws Exception {
            field.setAccessible( true );
            field.set( ins, value );
            if( collection != null && !collection.contains( ins ) ) {
                collection.add( ins );
            }
            field.setAccessible( false );
        }

        public Type getFieldType() {
            return fieldType;
        }

        public void setFieldType(Type fieldType) {
            this.fieldType = fieldType;
        }

        public Object getIns() {
            return ins;
        }

        public void setIns(Object ins) {
            this.ins = ins;
        }

        public Field getField() {
            return field;
        }

        public void setField(Field field) {
            this.field = field;
        }

        public Collection getCollection() {
            return collection;
        }

        public void setCollection(Collection collection) {
            this.collection = collection;
        }

        public IExcelObjInspector getInspector() {
            return inspector;
        }

        public void setInspector(IExcelObjInspector inspector) {
            this.inspector = inspector;
        }
    }

}
